﻿/// <reference path="../devextreme/ts/jquery.d.ts" />
/// <reference path="../devextreme/ts/dx.all.d.ts" />

($ => {
    class ImageListEditor {
        constructor(options) {
            this.options = options;
            this.images = [];

            this.init();
        }

        get currentImage() {
            return this._currentImage;
        }

        set currentImage(value) {
            if (value?.id !== this._currentImage?.id) {
                if (this._currentImage) {
                    this.getImageElement(this._currentImage)?.removeClass('current');
                }

                if (value) {
                    this.getImageElement(value)?.addClass('current');
                }

                this._currentImage = value;
            }
        }

        get previousImage() {
            if (this.images.length > 0) {
                if (this.currentImage) {
                    var index = this.images.findIndex(image => image.id === this.currentImage.id);
                    if (index > 0) {
                        return this.images[index - 1];
                    }
                } else {
                    return this.images[0];
                }
            }
        }

        get nextImage() {
            if (this.images.length > 0) {
                if (this.currentImage) {
                    var index = this.images.findIndex(image => image.id === this.currentImage.id);
                    if (index < this.images.length - 1) {
                        return this.images[index + 1];
                    }
                } else {
                    return this.images[this.images.length - 1];
                }
            }
        }

        get selectedImages() {
            return this.images.filter(image => image.selected);
        }

        init() {
            this.$editor = this.createImageListEditor();
            this.scrollView = this.$editor.dxScrollView('instance');
            this.$imageList = this.$editor.find('.dx-scrollview-content');
            this.$editor.find('.dx-scrollable-content').on('click', e => {
                if (e.target.classList.contains('dx-scrollview-content') || e.target.classList.contains('dx-scrollable-content')) {
                    this.clearSelection();
                }
            });
            new Sortable(this.$imageList.get(0), {
                sort: !!this.options.sortable,
                group: {
                    name: 'image-list',
                    pull: 'clone',
                    put: !!this.options.sortable
                },
                onEnd: e => {
                    if (e.to === e.from && this.images.length > 0) {
                        if (this.options.onSortReportImage instanceof Function) {
                            let order = 0;
                            const oldIndex = e.oldIndex;
                            const newIndex = e.newIndex;
                            const image = this.images[oldIndex];
                            if (newIndex === 0) {
                                order = this.images[0].order - 1000;
                            } else if (newIndex === this.images.length - 1) {
                                order = this.images[this.images.length - 1].order + 1000;
                            } else {
                                order = (this.images[newIndex - 1].order + this.images[newIndex + 1].order) / 2;
                            }
                            image.order = parseInt(order);
                            this.images.sort((x, y) => x.order - y.order);
                            this.options.onSortReportImage(image.id, image.order);
                        }
                    }
                },
                onStart: e => {
                    const $item = $(e.item);
                    const imageId = $item.attr('image-id');
                    const image = this.images.find(image => image.id === imageId);
                    if (image) {
                        if (!image.selected || !this.options.multiDraggable) {
                            this.clearSelection();
                            this.selectImage(image);
                        }
                        const json = JSON.stringify(this.selectedImages.map(image => image.id));
                        $item.data('drag-data', json);
                    }
                },
                onAdd: e => {
                    if (e.to !== e.from) {
                        const $item = $(e.item);
                        const imageIds = JSON.parse($item.data('drag-data'));
                        $item.remove();
                        if (this.options.onGetImagesByIds instanceof Function) {
                            const images = this.options.onGetImagesByIds(imageIds);
                            if (this.options.onInsertImages instanceof Function) {
                                this.options.onInsertImages(images, e.newIndex);
                            }
                        }
                    }
                }
            });
        }

        onImagesChanged() {
            this.scrollView.update();
            this.updateEmptyText();

            if (this.options.onImagesChanged instanceof Function) {
                this.options.onImagesChanged(this.images);
            }
        }

        getImagesByIds(imageIds) {
            if (this.options.onGetImagesByIds instanceof Function) {
                return this.options.onGetImagesByIds(imageIds) ?? [];
            }
            return [];
        }

        getImage(image) {
            return this.images.find(value => value.id == image?.id);
        }

        getMaxOrder() {
            if (this.images.length > 0) {
                const images = this.images.sort((x, y) => x.order - y.order);
                return images[images.length - 1].order;
            }
            return 0;
        }

        getCenterOrder(startIndex, endIndex) {
            if (this.images.length === 0) {
                return 1000;
            } else if (this.images.length === 1) {
                return this.images[0].order + (endIndex > 0 ? 1000 : -1000);
            } else {
                startIndex = Math.max(0, startIndex);
                endIndex = Math.min(this.images.length - 1, endIndex);
                if (startIndex === endIndex) {
                    if (startIndex === 0) {
                        return this.images[startIndex].order - 1000;
                    } else if (startIndex === this.images.length - 1) {
                        return this.images[startIndex].order + 1000;
                    } else {
                        return 1000;
                    }
                } else {
                    return parseInt((this.images[endIndex].order + this.images[startIndex].order) / 2);
                }
            }
        }

        addImage(image) {
            if (!this.updateImage(image) && (image.url || image.data)) {
                if (this.options.sortable) {
                    image.order = this.getMaxOrder() + 1000;
                }
                if (this.images.findIndex(item => item.id === image.id) < 0) {
                    this.images.push(image);
                }
                this.$imageList.append(this.createImageElement(image));
                this.onImagesChanged();
            }
        }

        insertImage(image, index) {
            if (this.images.length > 0) {
                if (this.options.sortable) {
                    image.order = this.getCenterOrder(index - 1, index);
                }
                this.images.splice(index, 0, image);
                this.$imageList.append(this.createImageElement(image));
                const children = this.$imageList.children();
                if (index < this.images.length - 1) {
                    children.eq(index).before(children.eq(this.images.length - 1));
                }
                this.onImagesChanged();
            } else {
                this.addImage(image);
            }
        }

        updateImage(image) {
            const index = this.images.findIndex(item => item.id === image.id);
            if (index >= 0) {
                image = {
                    ...this.images[index],
                    ...image,
                };
                this.images[index] = image;
                return this.updateImageElement(image);
            }
            return false;
        }

        removeImage(image) {
            this.images = this.images.filter(x => x.id !== image.id);
            this.getImageElement(image)?.remove();
            this.onImagesChanged();
        }

        clearImages() {
            this.images = [];
            this._currentImage = null;
            this.$imageList.empty();
            this.onImagesChanged();
        }

        selectImage(image) {
            image.selected = true;
            this.getImageElement(image)?.addClass('selected');
        }

        selectImages(images) {
            for (const image of images) {
                this.selectImage(image);
            }
        }

        selectImagesByIndex(startIndex, endIndex) {
            if (startIndex > endIndex) {
                [startIndex, endIndex] = [endIndex - 1, startIndex + 1];
            }
            const images = this.images.slice(startIndex, endIndex);
            this.clearSelection();
            this.selectImages(images);
        }

        unselectImage(image) {
            image.selected = false;
            this.getImageElement(image)?.removeClass('selected');
        }

        invertSelect() {
            for (const image of this.images) {
                if (image.selected) {
                    this.unselectImage(image);
                } else {
                    this.selectImage(image);
                }
            }
        }

        selectAll() {
            for (const image of this.images) {
                this.selectImage(image);
            }
        }

        clearSelection() {
            for (const image of this.images) {
                this.unselectImage(image);
            }
        }

        gotoTop() {
            if (this.images.length > 0) {
                const $imageElement = this.getImageElement(this.images[0]);
                this.scrollView.scrollToElement($imageElement);
            }
        }

        gotoCurrent() {
            if (this.currentImage) {
                const $imageElement = this.getImageElement(this.currentImage);
                this.scrollView.scrollToElement($imageElement);
            }
        }

        gotoBottom() {
            if (this.images.length > 0) {
                const $imageElement = this.getImageElement(this.images[this.images.length - 1]);
                this.scrollView.scrollToElement($imageElement);
            }
        }

        scrollToImage(image) {
            if (image) {
                const $imageElement = this.getImageElement(image);
                this.scrollView.scrollToElement($imageElement);
            }
        }

        createImageElement(image) {
            if (this.options.onCreateImageElement instanceof Function) {
                const imageId = image.id;
                const $image = this.options.onCreateImageElement(image);
                $image.on('click', (e) => {
                    const image = this.images.find(i => i.id === imageId);
                    if (image) {
                        if (e.shiftKey) {
                            const startIndex = this.images.findIndex(x => x.selected);
                            const endIndex = this.images.findIndex(x => x.id === imageId);
                            this.selectImagesByIndex(startIndex, endIndex + 1);
                        } else if (e.ctrlKey) {
                            if (image.selected) {
                                this.unselectImage(image);
                            } else {
                                this.selectImage(image);
                            }
                        } else {
                            this.clearSelection();
                            this.selectImage(image);
                        }

                        if (image.selected) {
                            this.currentImage = image;
                        } else if (this.selectedImages instanceof Array && this.selectedImages.length > 0) {
                            this.currentImage = this.selectedImages[0];
                        } else {
                            this.currentImage = null;
                        }

                        if (this.options.onImageClicked instanceof Function) {
                            this.options.onImageClicked(image);
                        }
                    }
                });
                this.$imageList.append($image);
            }
        }

        getImageElement(image) {
            if (this.options.onGetImageElement instanceof Function) {
                return this.options.onGetImageElement(image);
            }
        }

        updateImageElement(image) {
            if (this.options.onUpdateImageElement instanceof Function) {
                return this.options.onUpdateImageElement(image);
            }
        }

        updateEmptyText() {
            this.$editor.find('.empty-text').remove();
            if (this.images.length === 0) {
                $('<div class="empty-text" />')
                    .append($('<div class="text" />').text(this.options.emptyText ?? '暂无图像'))
                    .appendTo(this.$editor);
            }
        }

        refresh() {
            if (this.options.onGetImages instanceof Function) {
                this.clearImages();
                const $loadPanelContainer = $('<div />').appendTo(this.$editor);
                const loadPanel = $loadPanelContainer.dxLoadPanel({
                    shading: true,
                    position: {
                        of: this.$editor
                    }
                }).dxLoadPanel('instance');
                loadPanel.show();
                this.options.onGetImages().then(images => {
                    this.images = images;
                    for (const image of images) {
                        this.addImage(image);
                    }
                    if (this.options.onRefreshImageInfos instanceof Function) {
                        this.options.onRefreshImageInfos();
                    }
                }).finally(() => {
                    loadPanel.dispose();
                });
            }
        }

        createImageListEditor() {
            return $(`<div class="image-list-editor" />`).dxScrollView({
                showScrollbar: 'always',
            });
        }
    }

    class ImagePreview {
        constructor(options) {
            this.options = options;
        }

        init() {
            if (this.options.container) {
                this.$imagePreview = this.createImagePreview();
                $(this.options.container).append(this.createToolbar()).append(this.$imagePreview);
                this.imageViewerId = `id-${Date.now()}`;
                this.$imagePreview.attr('id', this.imageViewerId);
                this.imageViewer = OpenSeadragon({
                    id: this.imageViewerId,
                    prefixUrl: '/js/openseadragon/images/',
                    showRotationControl: true,
                    minZoomImageRatio: 0.1,
                    maxZoomPixelRatio: 10,
                    animationTime: 0,
                });
                this.imageViewer.addHandler('open', () => {
                    if (this.viewerState) {
                        this.imageViewer.viewport.zoomTo(this.viewerState.zoom);
                        this.imageViewer.viewport.panTo(this.viewerState.center);
                    }
                    if (this.firstOpen === undefined) {
                        this.firstOpen = false;
                        this.imageViewer.viewport.goHome();
                    }
                });
            }
        }

        previousImage() {
            if (this.options.onGetPreviousImage instanceof Function) {
                const image = this.options.onGetPreviousImage();
                if (image) {
                    if (this.options.onSetCurrentImage instanceof Function) {
                        this.options.onSetCurrentImage(image);
                    }
                    this.viewImage(image);
                }
            }
        }

        nextImage() {
            if (this.options.onGetNextImage instanceof Function) {
                const image = this.options.onGetNextImage();
                if (image) {
                    if (this.options.onSetCurrentImage instanceof Function) {
                        this.options.onSetCurrentImage(image);
                    }
                    this.viewImage(image);
                }
            }
        }

        renderImage(image) {
            if (image) {
                let imageUrl = image.url;
                if (!imageUrl && image.data) {
                    imageUrl = `data:image/jpeg;base64,${image.data}`;
                }

                if (this.viewerState) {
                    this.viewerState = {
                        zoom: this.imageViewer.viewport.getZoom(),
                        center: this.imageViewer.viewport.getCenter()
                    };
                }

                this.imageViewer.open({
                    type: 'image',
                    url: imageUrl
                });

                if (!this.viewerState) {
                    this.viewerState = {
                        zoom: this.imageViewer.viewport.getZoom(),
                        center: this.imageViewer.viewport.getCenter()
                    };
                }
            }
        }

        viewImage(image) {
            this.image = image;
            this.renderImage(image);
        }

        previewImage(image) {
            this.renderImage(image, true);
        }

        closePreviewImage() {
            this.viewImage(this.image);
        }

        createToolbar() {
            this.$toolbar = $('<div class="report-image-list-toolbar" />').dxToolbar({
                multiline: true,
                items: [
                    {
                        location: 'before',
                        template() {
                            return $('<div style="font-weight:bold;margin:0 10px 0 15px">图像预览</div>');
                        }
                    },
                    {
                        location: 'before',
                        widget: 'dxButton',
                        options: {
                            text: '上一张',
                            icon: 'chevronleft',
                            onClick: () => {
                                this.previousImage();
                            },
                        },
                    }, {
                        location: 'before',
                        widget: 'dxButton',
                        options: {
                            text: '下一张',
                            icon: 'chevronright',
                            onClick: () => {
                                this.nextImage();
                            },
                        },
                    }, {
                        location: 'before',
                        widget: 'dxButton',
                        options: {
                            text: '插入',
                            icon: 'add',
                            beginGroup: true,
                            onClick: () => {
                                if (this.image && this.options.onInsertReportImage instanceof Function) {
                                    this.options.onInsertReportImage(this.image);
                                }
                            },
                        },
                    },
                ],
            });

            return this.$toolbar;
        }

        createImagePreview() {
            return this.$imagePreview = $('<div class="image-preview" />');
        }

        setReadOnly(readOnly) {
            const toolbar = this.$toolbar.dxToolbar('instance');
            const tools = toolbar.option('items');
            for (const tool of tools) {
                if (tool.options?.text === '插入') {
                    tool.visible = !readOnly;
                }
            }
            toolbar.repaint();
        }
    }

    class ReportImageList {
        constructor(options) {
            this.options = options;
            this.init();
        }

        get images() {
            return this.imageListEditor.images;
        }

        init() {
            if (this.options.container) {
                this.imageListEditor = new ImageListEditor({
                    ...this.options,
                    sortable: true,
                    onCreateImageElement: this.createImageElement.bind(this),
                    onUpdateImageElement: this.updateImageElement.bind(this),
                    onGetImageElement: this.getImageElement.bind(this),
                    onGetImages: this.getImages.bind(this),
                    onSortReportImage: this.options.onSortReportImage.bind(this),
                    onRefreshImageInfos: this.refreshImageNumber.bind(this),
                    onInsertImages: (images, index) => {
                        this.insertImages(images, index);
                        this.refreshImageNumber();
                    },
                    onImageClicked: () => this.viewImage(this.imageListEditor.currentImage)
                });
                $(this.options.container).append(this.createToolbar()).append(this.imageListEditor.$editor);
            }
        }

        async getImages() {
            this.options.imagePositions = await this.options.onGetReportImagePositions();
            this.options.imageDescriptions = await this.options.onGetReportImageDescriptions();
            return await this.options.onGetReportImages();
        }

        addImages(images) {
            const addedImages = [];
            for (const image of images) {
                const newImage = { ...image, selected: false };
                const oldImage = this.images.find(x => x.id === newImage.id);
                if (oldImage) {
                    this.imageListEditor.selectImage(oldImage);
                } else {
                    addedImages.push(newImage);
                    this.imageListEditor.addImage(newImage);
                }
            }
            return addedImages;
        }

        insertImages(images, index) {
            var insertedImages = [];
            for (let i = images.length - 1; i >= 0; i--) {
                const newImage = { ...images[i], selected: false };
                const oldImage = this.images.find(x => x.id === newImage.id);
                if (oldImage) {
                    this.imageListEditor.selectImage(oldImage);
                } else {
                    insertedImages.push(newImage);
                    this.imageListEditor.insertImage(newImage, index);
                }
            }
            if (insertedImages.length > 0) {
                if (this.options.onInsertReportImages instanceof Function) {
                    this.options.onInsertReportImages(insertedImages);
                }
            }
            return insertedImages;
        }

        updateImages(images) {
            const updatedImages = [];
            for (const image of images) {
                if (this.imageListEditor.updateImage(image) && image.url) {
                    updatedImages.push(image);
                }
            }
            if (updatedImages.length > 0) {
                if (this.options.onInsertImages instanceof Function) {
                    this.options.onInsertImages(updatedImages);
                }
            }
        }

        removeImages(images) {
            for (const image of images) {
                this.imageListEditor.removeImage(image);
            }
        }

        createImageElement(image) {
            const imageId = image.id;
            const $imageItem = $(`<div class="image-item image-item-${imageId}" image-id="${imageId}" />`);

            const $image = $('<img class="image" crossorigin="anonymous" />');

            if (image.url) {
                $image.attr('src', image.url);
            } else if (image.data) {
                $image.attr('src', `data:image/jpeg;base64,${image.data}`);
            }

            $image.on('mouseenter', () => {
                const image = this.images.find(i => i.id === imageId);
                if (image) {
                    this.previewImage(image);
                }
            });

            $image.on('mouseleave', () => {
                this.closePreviewImage();
            });

            $imageItem.append($image);

            const $imageNumber = $('<div class="image-number" />');
            $imageNumber.text(this.images.length);
            $imageItem.append($imageNumber);

            let imagePosition;
            const imagePositions = this.options.imagePositions ?? [];

            if (image.position) {
                imagePosition = image.position;
            } else if (imagePositions.length > 0) {
                imagePosition = '选择位置';
            }

            if (imagePosition) {
                let imagePositionItemsContextMenu;
                const $imagePosition = $('<div class="image-position" />');
                if (imagePosition === '选择位置') {
                    $imagePosition.addClass('empty');
                }
                $imagePosition.text(imagePosition);
                $imagePosition.on('click', () => {
                    if (!this.options.readOnly) {
                        imagePositionItemsContextMenu?.show();
                    }
                });
                $imageItem.append($imagePosition);
                if (imagePositions.length > 0) {
                    const $imagePostionItems = $('<div class="image-position-items"/>');
                    $imageItem.append($imagePostionItems);
                    imagePositionItemsContextMenu = $imagePostionItems.dxContextMenu({
                        dataSource: this.options.imagePositions.map(p => { return { text: p } }),
                        showSubmenuMode: { name: 'onClick' },
                        target: $imagePosition,
                        onItemClick: (e) => {
                            const imagePosition = e.itemData.text;
                            this.options.onUpdateReportImagePosition(image.id, imagePosition).then(() => {
                                $imagePosition.text(imagePosition);
                                $imagePosition.removeClass('empty');
                                this.imageListEditor.updateImage({
                                    id: image.id,
                                    position: imagePosition
                                });
                            });
                        }
                    }).dxContextMenu('instance');
                }
            }

            let imageDescription;
            const imageDescriptions = this.options.imageDescriptions ?? [];

            if (image.description) {
                imageDescription = image.description;
            } else if (imageDescriptions.length > 0) {
                imageDescription = '选择描述';
            }

            if (imageDescription) {
                let imageDescriptionItemsContextMenu;
                const $imageDescription = $('<div class="image-description" />');
                $imageDescription.text(imageDescription);
                $imageDescription.on('click', () => {
                    if (!this.options.readOnly) {
                        imageDescriptionItemsContextMenu?.show();
                    }
                });
                $imageItem.append($imageDescription);
                if (imageDescriptions.length > 0) {
                    const $imageDescriptionItems = $('<div class="image-description-items"/>');
                    $imageItem.append($imageDescriptionItems);
                    const contextMenuDataSource = this.options.imageDescriptions.map(d => { return { text: d } });
                    if (contextMenuDataSource.filter(d => d.id === 'custom').length === 0) {
                        contextMenuDataSource.push({ id: 'custom', text: '自定义...' });
                    }
                    imageDescriptionItemsContextMenu = $imageDescriptionItems.dxContextMenu({
                        dataSource: contextMenuDataSource,
                        showSubmenuMode: { name: 'onClick' },
                        target: $imageDescription,
                        onItemClick: (e) => {
                            if (e.itemData.id === 'custom') {
                                $('<div/>').dxForm({
                                    items: [{
                                        dataField: 'description',
                                        label: {
                                            text: '图像描述',
                                        },
                                        editorOptions: {
                                            text: $imageDescription.text() === '选择描述' ? '' : $imageDescription.text()
                                        }
                                    }]
                                }).formPopup({
                                    title: '自定义图像描述',
                                    height: 235,
                                }).then(data => {
                                    this.options.onUpdateReportImageDescription(image.id, data.description).then(() => {
                                        $imageDescription.text(data.description ? data.description : '选择描述');
                                    });
                                });
                            } else {
                                $imageDescription.text(e.itemData.text);
                            }
                        }
                    }).dxContextMenu('instance');
                }
            }

            const $imageTools = $('<div class="tools" />');

            $imageTools.append($('<div class="tool tool-delete" />').on('click', () => {
                const image = this.images.find(i => i.id === imageId);
                if (image) {
                    this.deleteImages([image]);
                }
            }));

            return $imageItem;
        }

        updateImageElement(image) {
            const imageId = image.id;
            const $image = this.imageListEditor.$imageList.find(`.image-item-${imageId} > .image`);
            if ($image.length > 0) {
                if (image.url) {
                    $image.attr('src', image.url);
                } else if (image.data) {
                    $image.attr('src', `data:image/jpeg;base64,${image.data}`);
                }
                return true;
            }
        }

        getImageElement(image) {
            return this.imageListEditor.$imageList.find(`.image-item-${image.id}`);
        }

        viewImage(image) {
            if (this.options.onViewImage instanceof Function) {
                this.options.onViewImage(image);
            }
        }

        previewImage(image) {
            if (this.options.onPreviewImage instanceof Function) {
                this.options.onPreviewImage(image);
            }
        }

        closePreviewImage() {
            if (this.options.onClosePreviewImage instanceof Function) {
                this.options.onClosePreviewImage();
            }
        }

        deleteImages(images) {
            images ??= [];
            if (images.length > 0) {
                if (this.options.onDeleteReportImages instanceof Function) {
                    this.options.onDeleteReportImages(images).then(() => {
                        this.removeImages(images);
                        this.refreshImageNumber();
                    });
                }
            }
        };

        deleteSelectedImages() {
            this.deleteImages(this.imageListEditor.selectedImages);
        }

        clearImages() {
            if (this.options.onClearReportImages instanceof Function) {
                this.options.onClearReportImages().then(() => {
                    this.refresh();
                });
            }
        }

        processSelectedImages() {
            const images = this.imageListEditor.selectedImages ?? [];
            if (images.length > 0) {
                if (this.options.onProcessImages instanceof Function) {
                    this.options.onProcessImages(images.map(image => {
                        return {
                            imageId: image.id,
                            imageData: image.url ?? image.data
                        }
                    })).then(data => {
                        for (const item of data) {
                            if (item.data) {
                                const image = this.images.find(i => i.id === item.imageId);
                                if (image) {
                                    image.url = null;
                                    image.data = item.data;
                                    this.updateImageElement(image);
                                    if (this.options.onUpdateReportImage instanceof Function) {
                                        this.options.onUpdateReportImage(image.id, image.data);
                                    }
                                }
                            }
                        }
                    });
                }
            }
        }

        getImagePixelData(image) {
            const imageId = image.id;
            const $image = this.imageListEditor.$imageList.find(`.image-item-${imageId} > .image`);
            if ($image.length > 0) {
                const canvas = document.createElement('canvas');
                canvas.width = $image[0].naturalWidth;
                canvas.height = $image[0].naturalHeight;
                const context = canvas.getContext('2d');
                context.drawImage($image[0], 0, 0);
                return canvas.toDataURL("image/jpeg");
            }
        }

        invertImage(image) {
            if (image && this.options.onInvertImage instanceof Function) {
                let imageData = null;
                if (image.data) {
                    imageData = `data:image/jpeg;base64,${image.data}`;
                } else {
                    imageData = this.getImagePixelData(image);
                }
                if (imageData) {
                    this.options.onInvertImage(imageData).then(data => {
                        if (data) {
                            image.url = null;
                            image.data = data;
                            this.updateImageElement(image);
                            if (this.options.onUpdateReportImage instanceof Function) {
                                this.options.onUpdateReportImage(image.id, data);
                            }
                        }
                    });
                }
            }
        }

        invertSelectedImages() {
            const images = this.imageListEditor.selectedImages ?? [];
            for (const image of images) {
                this.invertImage(image);
            }
        }

        refreshImageNumber() {
            this.imageListEditor.$imageList.find('.image-number').each((index, element) => {
                $(element).text(index + 1);
            });
        }

        refreshImageInfo(image) {
            const $image = this.imageListEditor.$imageList.find(`.image-item-${image.id}`);
            $image.find('.image-position').text(image.position);
            $image.find('.image-description').text(image.description);
        }

        refreshImageInfos() {
            for (const image of this.imageListEditor.images) {
                this.refreshImageInfo(image);
            }
        }

        refresh() {
            this.imageListEditor.refresh();
        }

        updateImageListTitle() {
            let title = '打印图像';
            if (this.imageListEditor.images && this.imageListEditor.images.length > 0) {
                title = `${title}(${this.imageListEditor.images.length}个)`;
            }
            $(this.options.container).find('.image-list-title').text(title);
        }

        createToolbar() {
            this.$toolbar = $('<div class="image-list-toolbar" />').dxToolbar({
                multiline: true,
                items: [
                    {
                        location: 'before',
                        template() {
                            return $('<div class="image-list-title" style="font-weight:bold;margin:0 10px 0 15px">打印图像</div>');
                        }
                    },
                    {
                        location: 'before',
                        widget: 'dxButton',
                        options: {
                            text: '移除',
                            icon: 'remove',
                            beginGroup: true,
                            onClick: () => {
                                this.deleteSelectedImages();
                            },
                        },
                    }, {
                        location: 'before',
                        widget: 'dxButton',
                        options: {
                            text: '清空',
                            icon: 'trash',
                            beginGroup: true,
                            onClick: () => {
                                this.clearImages();
                            },
                        },
                    }, {
                        location: 'before',
                        widget: 'dxDropDownButton',
                        options: {
                            text: '处理',
                            dropDownOptions: {
                                width: 120,
                            },
                            items: [
                                {
                                    text: '反相',
                                    icon: 'md_invert_colors',
                                    onClick: () => {
                                        this.invertSelectedImages();
                                    },
                                }, {
                                    text: '更多',
                                    icon: 'antd_image',
                                    onClick: () => {
                                        this.processSelectedImages();
                                    },
                                }
                            ],
                        },
                    }, {
                        location: 'after',
                        widget: 'dxButton',
                        options: {
                            text: '刷新',
                            icon: 'refresh',
                            onClick: () => {
                                this.refresh();
                            },
                        },
                    },
                ],
            });
            return this.$toolbar;
        }

        setReadOnly(readOnly) {
            const toolbar = this.$toolbar.dxToolbar('instance');
            const tools = toolbar.option('items');
            this.options.readOnly = readOnly;
            for (const tool of tools) {
                if (tool.options?.text === '移除'
                    || tool.options?.text === '清空'
                    || tool.options?.text === '处理') {
                    tool.visible = !readOnly;
                }
            }
            toolbar.repaint();
        }
    }

    class ImageList {
        constructor(options) {
            this.options = options;
            this.init();
        }

        get images() {
            return this.imageListEditor.images;
        }

        init() {
            if (this.options.container) {
                this.imageListEditor = new ImageListEditor({
                    ...this.options,
                    multiDraggable: true,
                    onCreateImageElement: this.createImageElement.bind(this),
                    onUpdateImageElement: this.updateImageElement.bind(this),
                    onGetImageElement: this.getImageElement.bind(this),
                    onRefreshImageInfos: this.refreshImageNumber.bind(this),
                    onImageClicked: () => this.viewImage(this.imageListEditor.currentImage)
                });
                $(this.options.container).append(this.createToolbar()).append(this.imageListEditor.$editor);
            }
        }

        addImages(images) {
            for (const image of images) {
                this.imageListEditor.addImage({ ...image, selected: false });
            }
            if (window.localStorage.getItem('image-list-auto-scroll-to-bottom') !== 'false') {
                this.imageListEditor.gotoBottom();
            }
        }

        updateImages(images) {
            for (const image of images) {
                this.imageListEditor.updateImage({ ...image, selected: false });
            }
        }

        removeImages(images) {
            for (const image of images) {
                this.imageListEditor.removeImage(image);
            }
        }

        createImageElement(image) {
            const imageId = image.id;
            const $imageItem = $(`<div class="image-item image-item-${imageId}" image-id="${imageId}" />`);

            const $image = $('<img class="image" crossorigin="anonymous" />');

            if (image.url) {
                $image.attr('src', image.url);
            } else if (image.data) {
                $image.attr('src', `data:image/jpeg;base64,${image.data}`);
            }

            $image.on('mouseenter', () => {
                const image = this.images.find(i => i.id === imageId);
                if (image) {
                    this.previewImage(image);
                }
            });

            $image.on('mouseleave', () => {
                this.closePreviewImage();
            });

            $imageItem.append($image);

            const $imageNumber = $('<div class="image-number" />');
            $imageNumber.text(this.images.findIndex(x => x.id === image.id) + 1);
            $imageItem.append($imageNumber);

            if (!image.url && image.data) {
                const $imageUnstoreFlag = $('<div class="image-unstore-flag" />');
                $imageItem.append($imageUnstoreFlag);
            }

            const $imageTools = $('<div class="tools" />');

            $imageTools.append($('<div class="tool tool-insert" />').on('click', () => {
                const image = this.images.find(i => i.id === imageId);
                if (image) {
                    this.insertImages([image]);
                }
            }));

            $imageTools.append($('<div class="tool tool-delete" />').on('click', () => {
                const image = this.images.find(i => i.id === imageId);
                if (image) {
                    this.deleteImages([image]);
                }
            }));

            return $imageItem;
        }

        updateImageElement(image) {
            const imageId = image.id;
            const $image = this.imageListEditor.$imageList.find(`.image-item-${imageId} > .image`);
            if ($image.length > 0) {
                if (image.url) {
                    $image.attr('src', image.url);
                } else if (image.data) {
                    $image.attr('src', `data:image/jpeg;base64,${image.data}`);
                }
                return true;
            }
        }

        notificationImageStored(image) {
            const imageId = image.id;
            const $image = this.imageListEditor.$imageList.find(`.image-item-${imageId} > .image`);
            if ($image.length > 0) {
                $image.parent().find('.image-unstore-flag').remove();
            }
        }

        getImageElement(image) {
            return this.imageListEditor.$imageList.find(`.image-item-${image.id}`);
        }

        viewImage(image) {
            if (this.options.onViewImage instanceof Function) {
                this.options.onViewImage(image);
            }
        }

        previewImage(image) {
            if (this.options.onPreviewImage instanceof Function) {
                this.options.onPreviewImage(image);
            }
        }

        closePreviewImage() {
            if (this.options.onClosePreviewImage instanceof Function) {
                this.options.onClosePreviewImage();
            }
        }

        insertImages(images) {
            images ??= [];
            if (images.length > 0) {
                if (this.options.onInsertReportImages instanceof Function) {
                    this.options.onInsertReportImages(images);
                }
            }
        }

        importImages() {
            if (this.options.onImportImages instanceof Function) {
                this.options.onImportImages();
            }
        }

        exportSelectedImages() {
            if (this.options.onExportImages instanceof Function) {
                this.options.onExportImages(this.imageListEditor.selectedImages);
            }
        }

        deleteImages(images) {
            images ??= [];
            if (images.length > 0) {
                if (this.options.onDeleteImages instanceof Function) {
                    DevExpress.ui.dialog.confirm('是否要删除所选择的图像?', '删除').done(result => {
                        if (result) {
                            this.options.onDeleteImages(images).then(() => {
                                for (const image of images) {
                                    this.imageListEditor.removeImage(image);
                                }
                                this.viewImage(null);
                                this.refreshImageNumber();
                            });
                        }
                    });
                }
            }
        };

        insertSelectedImages() {
            this.insertImages(this.imageListEditor.selectedImages);
        }

        deleteSelectedImages() {
            this.deleteImages(this.imageListEditor.selectedImages);
        }

        refreshImageNumber() {
            this.imageListEditor.$imageList.find('.image-number').each((index, element) => {
                $(element).text(index + 1);
            });
        }

        refresh() {
            this.imageListEditor.refresh();
        }

        updateImageListTitle() {
            let title = '图像列表';
            if (this.imageListEditor.images && this.imageListEditor.images.length > 0) {
                title = `${title}(${this.imageListEditor.images.length}个)`;
            }
            $(this.options.container).find('.image-list-title').text(title);
        }

        createToolbar() {
            this.$toolbar = $('<div class="image-list-toolbar" />').dxToolbar({
                multiline: true,
                items: [
                    {
                        location: 'before',
                        template() {
                            return $('<div class="image-list-title" style="font-weight:bold;margin:0 10px 0 15px">图像列表</div>');
                        }
                    },
                    {
                        location: 'before',
                        widget: 'dxButton',
                        options: {
                            text: '插入',
                            icon: 'add',
                            onClick: () => {
                                this.insertSelectedImages();
                            },
                        },
                    }, {
                        location: 'before',
                        widget: 'dxButton',
                        options: {
                            text: '删除',
                            icon: 'remove',
                            beginGroup: true,
                            onClick: () => {
                                this.deleteSelectedImages();
                            },
                        },
                    }, {
                        location: 'before',
                        widget: 'dxDropDownButton',
                        options: {
                            text: '选择',
                            icon: 'check',
                            dropDownOptions: {
                                width: 150,
                            },
                            items: [
                                {
                                    text: '全部选择',
                                    icon: 'selectall',
                                    onClick: () => {
                                        this.imageListEditor.selectAll();
                                    },
                                }, {
                                    text: '反向选择',
                                    icon: 'unselectall',
                                    onClick: () => {
                                        this.imageListEditor.invertSelect();
                                    },
                                }, {
                                    text: '取消选择',
                                    icon: 'clear',
                                    onClick: () => {
                                        this.imageListEditor.clearSelection();
                                    },
                                },
                            ],
                        },
                    }, {
                        location: 'before',
                        widget: 'dxDropDownButton',
                        options: {
                            text: '跳转',
                            icon: 'arrowback',
                            dropDownOptions: {
                                width: 150,
                            },
                            items: [
                                {
                                    template: `<div class="dx-list-item-icon-container image-list-goto-top"><i class="dx-icon dx-icon-download dx-list-item-icon"></i></div><span>跳转到顶部</span>`,
                                    onClick: () => {
                                        this.imageListEditor.gotoTop();
                                    },
                                }, {
                                    text: '跳转到当前',
                                    icon: 'arrowright',
                                    onClick: () => {
                                        this.imageListEditor.gotoCurrent();
                                    },
                                }, {
                                    text: '跳转到底部',
                                    icon: 'download',
                                    onClick: () => {
                                        this.imageListEditor.gotoBottom();
                                    },
                                }, {
                                    text: '自动滚到底',
                                    icon: window.localStorage.getItem('image-list-auto-scroll-to-bottom') !== 'false' ? "check" : 'minus',
                                    onClick: () => {
                                        const toolbar = this.$toolbar.dxToolbar('instance');
                                        const items = toolbar.option('items');
                                        const tool = items.filter(item => item.options && item.options.text === '跳转')[0];
                                        const autoScrollToBottom = window.localStorage.getItem('image-list-auto-scroll-to-bottom');
                                        if (autoScrollToBottom === 'true') {
                                            window.localStorage.setItem('image-list-auto-scroll-to-bottom', 'false');
                                            tool.options.items[3].icon = 'minus';
                                        } else {
                                            window.localStorage.setItem('image-list-auto-scroll-to-bottom', 'true');
                                            tool.options.items[3].icon = 'check';
                                        }
                                        toolbar.option('items', items);
                                    },
                                },
                            ],
                        },
                    }, {
                        location: 'before',
                        widget: 'dxDropDownButton',
                        options: {
                            text: '更多',
                            dropDownOptions: {
                                width: 150,
                            },
                            items: [
                                {
                                    text: '导入',
                                    icon: 'import',
                                    onClick: () => {
                                        this.importImages();
                                    },
                                }, {
                                    text: '导出',
                                    icon: 'export',
                                    onClick: () => {
                                        this.exportSelectedImages();
                                    },
                                }
                            ],
                        },
                    }, {
                        location: 'after',
                        widget: 'dxButton',
                        options: {
                            text: '刷新',
                            icon: 'refresh',
                            onClick: () => {
                                this.refresh();
                            },
                        },
                    },
                ],
            });
            return this.$toolbar;
        }

        setReadOnly(readOnly) {
            const toolbar = this.$toolbar.dxToolbar('instance');
            const tools = toolbar.option('items');
            for (const tool of tools) {
                if (tool.options?.text === '插入' || tool.options?.text === '删除' || tool.options?.text === '选择') {
                    tool.visible = !readOnly;
                } else if (tool.options?.text === '更多') {
                    for (const toolItem of tool.options.items) {
                        if (toolItem.text === '导入') {
                            toolItem.visible = !readOnly;
                        }
                    }
                }
            }
            toolbar.repaint();
        }
    }

    $.fn.reportImageList = function (options) {
        options = options || {};

        const $container = $(this[0]).addClass('image-list-main');
        const $imagePreview = $('<div class="image-preview-container" />');
        const $reportImageList = $('<div class="report-image-list-container" />');
        const $imageList = $('<div class="image-list-container" />');

        let imageList, reportImageList, imagePreview;

        const insertImages = async images => {
            if (images && images.length > 0 && options.onInsertImages instanceof Function) {
                const insertImageTasks = images.filter(image => image.data).map(async image => {
                    await options.onInsertImages([image]);
                });
                const imagesWithoutData = images.filter(image => !image.data);
                if (imagesWithoutData.length > 0) {
                    insertImageTasks.push(options.onInsertImages(imagesWithoutData));
                }
                if (insertImageTasks.length > 0) {
                    await Promise.all(insertImageTasks);
                }
            }
        };

        imageList = new ImageList({
            ...options,
            emptyText: '采集的图像将会显示在这里',
            container: $imageList,
            onGetImages: options.onGetImages,
            onViewImage: image => imagePreview.viewImage(image),
            onGetImagesByIds: imageIds => {
                return imageList.images.filter(image => imageIds.indexOf(image.id) >= 0);
            },
            onImagesChanged: () => imageList.updateImageListTitle(),
            onPreviewImage: image => imagePreview.previewImage(image),
            onClosePreviewImage: () => imagePreview.closePreviewImage(),
            onInsertReportImages: async images => {
                await insertImages(reportImageList.addImages(images));
            },
            onDeleteImages: async images => {
                if (options.onDeleteImages instanceof Function) {
                    await options.onDeleteImages(images);
                    reportImageList.deleteImages(images);
                }
            },
        });

        reportImageList = new ReportImageList({
            ...options,
            emptyText: '从图像列表中拖动或插入图像到这里',
            container: $reportImageList,
            onGetImages: options.onGetReportImages,
            onGetImagesByIds: imageIds => {
                return imageList.images.filter(image => imageIds.indexOf(image.id) >= 0);
            },
            onViewImage: image => {
                imagePreview.viewImage(image);
                imageList.imageListEditor.clearSelection();
                image = imageList.imageListEditor.getImage(image);
                imageList.imageListEditor.currentImage = image;
                imageList.imageListEditor.selectImage(image);
                imageList.imageListEditor.scrollToImage(image);
            },
            onInsertReportImages: async images => {
                await insertImages(images);
            },
            onImagesChanged: () => reportImageList.updateImageListTitle(),
            onPreviewImage: image => imagePreview.previewImage(image),
            onClosePreviewImage: () => imagePreview.closePreviewImage(),
            onDeleteImages: async images => {
                if (options.onDeleteReportImages instanceof Function) {
                    await options.onDeleteReportImages(images);
                    reportImageList.removeImages(images);
                }
            },
        });

        imagePreview = new ImagePreview({
            ...options,
            container: $imagePreview,
            onGetPreviousImage: () => imageList.imageListEditor.previousImage,
            onGetNextImage: () => imageList.imageListEditor.nextImage,
            onSetCurrentImage: image => {
                [imageList, reportImageList].forEach(imageList => {
                    imageList.imageListEditor.currentImage = image;
                    imageList.imageListEditor.clearSelection();
                    imageList.imageListEditor.selectImage(image);
                    imageList.imageListEditor.scrollToImage(image);
                });
            },
            onInsertReportImage: async image => {
                await insertImages(reportImageList.addImages([image]));
            },
        });

        const $panel = $('<div style="height: 100%;" />').append($reportImageList).append($imageList).splitter({
            orientation: 'h',
            panel1Size: '170px'
        });

        $container.append($imagePreview).append($panel).splitter({
            orientation: 'h',
            panel2Size: '55%',
        });

        imagePreview.init();

        return {
            addImages: images => imageList.addImages(images),
            viewImage: image => imagePreview.viewImage(image),
            notificationImageStored: image => imageList.notificationImageStored(image),
            refresh: () => {
                reportImageList.refresh();
                imageList.refresh();
            },
            setReadOnly: readOnly => {
                imagePreview.setReadOnly(readOnly);
                reportImageList.setReadOnly(readOnly);
                imageList.setReadOnly(readOnly);
            },
        };
    }
})(jQuery);